home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 June
/
Macworld (1999-06).dmg
/
Shareware World
/
Info
/
For Developers
/
MacZoop2.0.sea
/
MacZoop2.0
/
Required Classes
/
ZEventHandler.cpp
< prev
next >
Wrap
Text File
|
1999-02-23
|
22KB
|
851 lines
/*************************************************************************************************
*
*
* MacZoop - "the framework for the rest of us"
*
*
*
* ZEventHandler.cpp -- the event handler object
*
*
*
*
*
* © 1996, Graham Cox
*
*
*
*
*************************************************************************************************/
#include "ZEventHandler.h"
#include "MacZoop.h"
#include "ProjectSettings.h"
#include "ZWindow.h"
#include "ZCommander.h"
#include "ZDialog.h"
#include <LowMem.h>
#include <MixedMode.h>
// gSleep is the sleep period passed to WaitNextEvent
short gSleep = kFrontSleep;
// gCurHandler is the current ZCommander object that wants to handle commands. This will
// usually be the active window or the application
ZCommander* gCurHandler = NULL;
// prototype of handler function for apple-events- sends them up the command chain.
static pascal OSErr ZAEHandler ( AppleEvent* aeEvt, AppleEvent* reply, long refCon );
// global UPP to same
AEEventHandlerUPP gAEUPP = NewAEEventHandlerProc( ZAEHandler );
/*-------------------------------*** CONSTRUCTOR ***---------------------------------*/
ZEventHandler::ZEventHandler()
{
clicks = 0;
inBackground = FALSE;
epPPCYokeDown = 0;
gCurHandler = gApplication;
}
/*--------------------------*** ESTABLISHCURRENTHANDLER ***---------------------------*/
/*
set up the value of <gCurHandler> by querying the active window
----------------------------------------------------------------------------------------*/
void ZEventHandler::EstablishCurrentHandler()
{
ZWindow* zw = gWindowManager->GetTopWindow();
// we only dispatch Idle if the window is active.
if ( zw && zw->IsActive())
gCurHandler = zw->GetHandler(); // this method returns the handler within the window
// or the window itself.
else
gCurHandler = gApplication;
}
/*---------------------------------*** GETANEVENT ***---------------------------------*/
/*
gets the next event off the queue. If it's a null event, give idle time
----------------------------------------------------------------------------------------*/
void ZEventHandler::GetAnEvent( EventRecord* theEvent, const short mask )
{
// gets an event from the event queue and sets "theEvent" to it. On the PowerPC, WNE is
// an emulated trap and performance can be enhanced by calling it much less frequently
// than once per loop. If we're running on PowerPC, we use the "epPPCYokeDown" variable
// to count our main loops. If we do not call WNE, we pretend that the event is a NULL
// event. The default is 32 loops per event.
#if GENERATINGPOWERPC
Boolean isIdle;
// for some events, we want to immediately handle them. However, calling EventAvail
// offsets some of the advantage of doing this yokedown anyway, so we check this
// every four calls to try and keep the speed improvement as good as possible. As always
// this is a tradeoff between good responsiveness and fast execution.
if (( epPPCYokeDown % 4 ) == 0 )
{
if ( EventAvail( mDownMask + keyDownMask + updateMask, theEvent ))
epPPCYokeDown = 0;
}
if ( epPPCYokeDown <= 0 )
{
isIdle = ! WaitNextEvent( mask, theEvent, gSleep, NULL );
epPPCYokeDown = kPowerPCWNEYokeDown;
}
else
{
theEvent->what = nullEvent;
theEvent->message = 0;
theEvent->when = TickCount();
GetMouse( &theEvent->where );
LocalToGlobal( &theEvent->where );
theEvent->modifiers = GetModifiers();
// decrement loop counter
isIdle = TRUE;
epPPCYokeDown--;
}
// if main screen has changed depth, send a message to tell any interested party
// about it.
short sd = GetMainScreenDepth();
if ( sd != gApplication->msDepth )
{
gApplication->msDepth = sd;
gApplication->SendMessage( msgMainScreenDepthChanged, &sd );
}
if ( isIdle )
PassIdle();
#else
if (! WaitNextEvent( mask, theEvent, gSleep, NULL ))
PassIdle();
#endif
gApplication->HandleMBarHiding( theEvent->where );
// dispatch the cursor to the current window, so it's shape can be set.
if ( !inBackground )
{
// if control key down, show contextual menu cursor and do NOT dispatch to window
if ( gMacInfo.hasContextualMenus && ( theEvent->modifiers & controlKey ) == controlKey )
SetCursorShape( CONTEXT_MENU_CURSOR );
else
{
ZWindow* aZW = gWindowManager->LocateWindow( theEvent->where );
if ( aZW )
{
WindowPtr w = aZW->GetMacWindow();
// check to see that the window is not "windowshaded"- if
// it is, don't dispatch the cursor. If the window is shaded,
// its contRgn will be empty.
if (! EmptyRgn( ((WindowPeek) w)->contRgn ))
{
if ( aZW )
{
Point mouse;
GrafPtr savePort;
mouse = theEvent->where;
GetPort( &savePort );
SetPort( w );
GlobalToLocal( &mouse );
SetPort( savePort );
// handle balloon help for the window. We only need to consider the frontmost
// window or floaters since the system displays balloons for other windows
if ( aZW->Floats() ||
aZW == gApplication->GetFrontWindow())
DoBalloons( aZW, theEvent->where );
// if the point is inside the window, dispatch to the window,
// otherwise reset the cursor to an arrow. If the window is not the top
// window, the cursor is not dispatched, but reset, unless the window is
// a floater, in which case we do dispatch it if there not a modal dialog
// in front of it. The result is correct for what the user expects.
if ( PtInRect( mouse, &w->portRect ) &&
( aZW == gApplication->GetFrontWindow() ||
( aZW->Floats() && ! gWindowManager->IsDialog( gApplication->GetFrontWindow()))))
aZW->AdjustCursor( mouse, theEvent->modifiers );
else
gApplication->MouseNotInAnyWindow( theEvent->where );
}
}
else
gApplication->MouseNotInAnyWindow( theEvent->where );
}
else
gApplication->MouseNotInAnyWindow( theEvent->where );
}
}
}
/*-----------------------------*** DISPATCHANEVENT ***--------------------------------*/
/*
determine what sort of event it is and call the relevant handler method.
----------------------------------------------------------------------------------------*/
void ZEventHandler::DispatchAnEvent( EventRecord* theEvent )
{
// dispatches the event according to its type, etc
Boolean isAutoKey = FALSE;
char theKey;
FailNILParam( theEvent );
lastEvent = *theEvent;
// set up gCurHandler
EstablishCurrentHandler();
// dispatch the event to the handler
switch ( theEvent->what )
{
case mouseDown:
HandleMouseEvent( *theEvent );
break;
case activateEvt:
// following defined in Project Settings.h
#if _ACTIVATE_EVENTS_ARE_REAL
HandleWindowActivate( (WindowPtr) theEvent->message,
(theEvent->modifiers & activeFlag) );
#endif
break;
case autoKey:
isAutoKey = TRUE;
case keyDown:
theKey = theEvent->message & charCodeMask;
HandleKeyEvent( theKey, isAutoKey, theEvent->modifiers );
break;
case updateEvt:
HandleWindowUpdate((WindowPtr) theEvent->message );
break;
case diskEvt:
{
Point dPt = {100,100};
if( HiWord( theEvent->message ) != 0)
DIBadMount( dPt, theEvent->message );
break;
}
case kHighLevelEvent:
HandleHLEvent( *theEvent );
break;
case osEvt:
HandleOSEvent( *theEvent );
break;
}
}
/*------------------------------*** HANDLEKEYEVENT ***--------------------------------*/
/*
process keystrokes. Command-Key sequences are passed to the menu handler
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleKeyEvent( const char theKey, const Boolean isAutoKey, short modifiers )
{
long mSelect;
if ( modifiers & cmdKey )
{
// if this is a command-key combination, translate this to a
// menu command as if it were picked from the menu.
if (! isAutoKey)
{
gMenuBar->DimMenus();
// ask the command chain to reenable the relevant menu items
if ( gCurHandler )
gCurHandler->UpdateMenus();
// check for a keyboard shortcut. If there is one, dispatch it,
// otherwise pass the key to the command chain for processing
mSelect = MenuKey( theKey );
if ( HiWord( mSelect ) != 0 )
{
gMenuBar->DispatchCommand( mSelect );
return;
}
}
}
// add in autokey flag to modifiers, for the benefit of Type() methods...
if ( isAutoKey )
modifiers |= FromAutoKeyEvent;
// the user is typing- send the characters to the current commander
if ( gCurHandler )
gCurHandler->Type( theKey, modifiers );
}
/*-------------------------------*** COUNTCLICKS ***----------------------------------*/
/*
count up the number of rapid mouse-clicks to detect double-clicks, etc.
----------------------------------------------------------------------------------------*/
void ZEventHandler::CountClicks( const WindowPtr target, const long clickTicks, const Point globalMouse )
{
static WindowPtr prevTarget = NULL;
static long lastClickTicks = 0;
static Point lastClick = {0,0};
GrafPtr savePort;
Point gMouse;
gMouse = globalMouse;
GetPort( &savePort );
SetPort( target );
GlobalToLocal( &gMouse );
if (target == prevTarget)
{
// the target window is the same as the last click, is the click
// within the double-click period set by the user?
if ( clickTicks < lastClickTicks + GetDblTime())
{
// yes, the click is in time. Finally, we ask the window
// whether we should consider the click as being in the same place.
ZWindow* zW = GetZWindow( target );
if ( zW && zW->ClickInSamePlace( lastClick, gMouse ))
clicks++;
else
clicks = 1;
}
else
clicks = 1;
}
else
clicks = 1;
// store the click point, target and click time for the next time
lastClick = gMouse;
prevTarget = target;
lastClickTicks = clickTicks;
SetPort( savePort );
}
/*---------------------------*** HANDLEMOUSEEVENT ***---------------------------------*/
/*
handle mouse button clicks. This finds the target window or other part of the screen
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleMouseEvent( const EventRecord& theEvent )
{
WindowPtr targetWindow;
short partCode;
long mSelect;
Point mousePt;
ZWindow* zappWindow;
// find out what part of the screen the mouse was clicked on
partCode = FindWindow( theEvent.where, &targetWindow );
// if it was any sort of window, get the ZWindow object associated with it, if any.
if ( targetWindow )
zappWindow = GetZWindow( targetWindow );
switch (partCode)
{
case inDesk:
SysBeep( 1 );
break;
case inMenuBar:
// the user clicked the menubar.
gMenuBar->ClickMenuBar( theEvent.where );
break;
case inSysWindow:
SystemClick( &theEvent, targetWindow ); // system will handle it (DA window)
break;
case inContent:
if ( targetWindow ) // the user clicked in a window
{
// if control key down, do a contextual menu pop-up instead
if ( theEvent.modifiers & controlKey )
{
zappWindow->ContextualMenuClick( theEvent.where );
}
else
{
// if there's a modal dialog up, clicks in other windows result in a beep.
ZWindow* aZW = gApplication->GetFrontWindow();
ZDialog* aZD;
if ( aZW )
aZD = dynamic_cast<ZDialog*> ( aZW );
else
aZD = NULL;
Boolean dialogUp = ( aZD && aZD->IsModal());
CountClicks( targetWindow, theEvent.when, theEvent.where ); // double click?
// if the window is top window or top floater, pass the click to the window.
if( zappWindow &&
(zappWindow == gApplication->GetFrontWindow() ||
(zappWindow == gWindowManager->GetTopFloater() && ! dialogUp )))
{
// if the window is already active, pass the click to the window object
// in its local coordinate system.
mousePt = theEvent.where;
zappWindow->Focus();
GlobalToLocal( &mousePt );
zappWindow->Click( mousePt, theEvent.modifiers );
}
else
{
// if the window wasn't active, make it active. If there is a modal dialog in
// front, then this action is not permitted.
if ( dialogUp )
{
SysBeep( 1 );
break;
}
// otherwise, process the click
if ( zappWindow )
{
// make this window the active one
zappWindow->Select();
// if the window is a floater, ask it to process the click as well-
// floaters process "front" clicks even when they were not initially
// active, since floaters are all 'peers' of one another.
if ( zappWindow->Floats())
{
mousePt = theEvent.where;
zappWindow->Focus();
GlobalToLocal( &mousePt );
zappWindow->Click( mousePt, theEvent.modifiers );
}
}
}
}
}
break;
case inDrag:
{
// the user wants to drag the window. Only permit this if no
// modal dialog is in front of it, or the command key is down.
ZDialog* aZD = dynamic_cast<ZDialog*>( gApplication->GetFrontWindow());
if ( aZD &&
aZD->IsModal() &&
(targetWindow != aZD->GetMacWindow()) &&
! (theEvent.modifiers & cmdKey))
{
SysBeep( 1 );
break;
}
ZWindow* zw = GetZWindow( targetWindow );
gWindowManager->DragWindowOutline( zw, theEvent.where, theEvent.modifiers );
break;
}
case inGrow:
{
// the user wants to resize the window
Rect szRect;
if ( zappWindow )
{
zappWindow->GetSizeRect( &szRect );
szRect.right++;
szRect.bottom++;
mSelect = GrowWindow( targetWindow, theEvent.where, &szRect );
if ( mSelect )
{
// if the command key was down, then we are attempting to override the
// current settings for sizerect. Thus we need to change the size rect
// to the desired size first:
if ( theEvent.modifiers & cmdKey )
{
szRect.right = MAX( LoWord( mSelect ), szRect.right - 1 );
szRect.bottom = MAX( HiWord( mSelect ), szRect.bottom - 1 );
zappWindow->SetSizeRect( szRect );
}
zappWindow->SetSize( LoWord( mSelect ), HiWord( mSelect ));
}
}
break;
}
case inGoAway:
if ( TrackGoAway( targetWindow, theEvent.where ))
{
// if the user clicks in the go-away box, the window will be closed. If the
// option key is down, we close all of the windows.
if ( theEvent.modifiers & optionKey )
gApplication->CloseAll( zappWindow->Floats());
else
{
if ( zappWindow )
zappWindow->Close( gApplication->GetPhase());
}
}
break;
case inZoomIn:
case inZoomOut:
if( TrackBox( targetWindow, theEvent.where, partCode ))
{
// if the user clicked in the zoom box, the window will be zoomed
if( zappWindow )
zappWindow->Zoom( partCode );
}
break;
}
}
/*---------------------------*** HANDLEWINDOWUPDATE ***-------------------------------*/
/*
to handle an update event, call the window object's update method.
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleWindowUpdate( const WindowPtr theWindow )
{
ZWindow* zappWindow = NULL;
Boolean memState;
if ( theWindow )
zappWindow = GetZWindow( theWindow );
memState = gApplication->MemoryCrisis();
if ( zappWindow )
zappWindow->PerformUpdate();
// if the draw resulted in a memory shortage, suppress the warning, since
// that will trigger further updates, etc.
if ( gApplication->MemoryCrisis() != memState )
gApplication->userHasSeenAlert = TRUE;
}
/*---------------------------------*** PASSIDLE ***-----------------------------------*/
/*
send the idle command to the current handler, and pass it up the chain
----------------------------------------------------------------------------------------*/
void ZEventHandler::PassIdle()
{
// called repeatedly for null events. This passes idle messages to the current commander
EstablishCurrentHandler();
if ( gCurHandler )
gCurHandler->Idle();
// floaters may want idle time too, though they are not part of the command chain. We
// ask the window manager to give them time:
gWindowManager->FloatIdle();
}
/*--------------------------*** HANDLEWINDOWACTIVATE ***------------------------------*/
/*
calls activate/deactivate for window
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleWindowActivate( const WindowPtr theWindow, const Boolean state )
{
// handles the activate and deactivate events. This calls the window object with
// the activate messages, and also sets up gCurHandler.
ZWindow* zw = NULL;
if ( theWindow )
zw = GetZWindow( theWindow );
if ( zw )
{
zw->Focus();
if ( state )
zw->Activate();
else
zw->Deactivate();
}
}
/*------------------------------*** HANDLEOSEVENT ***---------------------------------*/
/*
handle the suspend and resume events sent by MultiFinder/System 7
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleOSEvent( const EventRecord& theEvent )
{
if ((( theEvent.message & 0xFF000000 ) >> 24 ) == suspendResumeMessage )
{
if ( theEvent.message & resumeFlag )
{
inBackground = FALSE;
gSleep = kFrontSleep;
if ( theEvent.message & convertClipboardFlag )
gClipboard->ConvertToPrivate();
gCurHandler->DoResume();
ResumeCursorAnimation();
}
else
{
inBackground = TRUE;
gSleep = kBackSleep;
gMenuBar->ShowHideMenuBar( MBAR_SHOW );
gClipboard->ConvertFromPrivate();
gCurHandler->DoSuspend();
PauseCursorAnimation( 0 );
}
SetCursorShape( ARROW_CURSOR );
}
}
/*------------------------------*** HANDLEHLEVENT ***---------------------------------*/
/*
handle the high level events sent by AOCE, Applescript, et al.
----------------------------------------------------------------------------------------*/
void ZEventHandler::HandleHLEvent( const EventRecord& theEvent)
{
// handles high level events such as applescript events, etc.
if ( theEvent.message == typeAppleEvent )
FailOSErr( AEProcessAppleEvent( &theEvent ));
else
gApplication->ProcessHLEvent( theEvent );
}
/*-----------------------*** INSTALLAPPLESCRIPTHANDLERS ***---------------------------*/
/*
install the handler vectors needed to field applescript events. BY default this installs
the four required events. You can override this to install more, or call the method
below as needed.
----------------------------------------------------------------------------------------*/
void ZEventHandler::InstallApplescriptHandlers()
{
InstallAppleEventHandler( kCoreEventClass, kAEOpenApplication );
InstallAppleEventHandler( kCoreEventClass, kAEOpenDocuments );
InstallAppleEventHandler( kCoreEventClass, kAEPrintDocuments );
InstallAppleEventHandler( kCoreEventClass, kAEQuitApplication );
// MacOS 8 has a new core apple event for reopening the application...
if ( gMacInfo.systemVersion >= 0x0800 )
InstallAppleEventHandler( kCoreEventClass, kAEReopenApplication );
}
/*------------------------*** INSTALLAPPLEEVENTHANDLER ***----------------------------*/
/*
install the handler vector needed to field all applescript events. Call this once for
each event class and ID you wish to process. Apple Events will be unpacked by the global
vector proc and sent up the command chain. You can intercept them there and process them
as needed.
----------------------------------------------------------------------------------------*/
void ZEventHandler::InstallAppleEventHandler( const AEEventClass pClass, const AEEventID pID )
{
// installs the AppleEvent hook for the given apple event
if ( gMacInfo.hasAppleEvents )
FailOSErr( AEInstallEventHandler( pClass, pID, gAEUPP, (long) gApplication, FALSE ));
}
/*-------------------------------*** DOBALLOONS ***-----------------------------------*/
/*
display balloons as mouse tracks over windows, etc.
----------------------------------------------------------------------------------------*/
void ZEventHandler::DoBalloons( ZWindow* aWindow, const Point globMouse )
{
HMMessageRecord hm;
Rect br;
Point tip, lm;
if ( ! HMIsBalloon() && HMGetBalloons())
{
lm = globMouse;
aWindow->Focus();
GlobalToLocal( &lm );
if ( aWindow->GetBalloonHelp( lm, &br, &tip, &hm ))
{
if ( PtInRect( lm, &br ))
aWindow->ShowBalloonHelp( &br, tip, &hm );
}
else
HMRemoveBalloon();
}
}
#pragma mark -
/*---------------------------------*********************---------------------------------*/
static pascal OSErr ZAEHandler( AppleEvent* aeEvt, AppleEvent* reply, long refCon )
{
// this is the global vector proc that is called for every apple event. This determines the
// event class and ID, then sends that info, plus the event, up the command chain, where,
// presumably, it will be handled.
AEEventClass pClass;
AEEventID pID;
DescType returnedType;
long returnedSize;
OSErr theErr = noErr;
try
{
// determine the AE class and ID (this is laborious, so it's done here once and passed on)
FailOSErr( AEGetAttributePtr( aeEvt, keyEventClassAttr,
typeType, &returnedType,
&pClass, sizeof( AEEventClass ), &returnedSize ));
FailOSErr( AEGetAttributePtr( aeEvt, keyEventIDAttr,
typeType, &returnedType,
&pID, sizeof( AEEventID ), &returnedSize ));
if ( gCurHandler )
{
gCurHandler->HandleAppleEvent( pClass, pID, aeEvt, reply );
theErr = noErr;
}
else
theErr = errAEEventNotHandled;
}
catch( OSErr err )
{
theErr = err;
}
return theErr;
}